package util;

import util.BlockingQueueTB.Consummer;
import util.BlockingQueueTB.Producer;

public class ThreadPool {

	private int NbMinThread, NbMaxThread;
	private BlockingQueueTB<Thread> threadsDuPool = new BlockingQueueTB<Thread>();
	private BlockingQueueTB<Runnable> jobQueue = new BlockingQueueTB<Runnable>();
	

	public ThreadPool(int NbMinThread, int NbMaxThread) {
		this.NbMinThread = NbMinThread;
		this.NbMaxThread = NbMaxThread;
	}

	
	synchronized public void start() {
		new LoadManagementThread().start();
		for (int i = 0; i < NbMinThread; i++) {
			threadsDuPool.enqueue(new ThreadDuPool());
			threadsDuPool.getAt(i).setName("Thread: "+i);
			threadsDuPool.getAt(i).start();
		}
	
	}
	
	@Producer
	public void execute(Runnable job) {
		jobQueue.enqueue(job);
	}
	

	private class ThreadDuPool extends Thread {

		@Consummer
		public void run() {
			while (true) {
				Runnable jobTodo = jobQueue.dequeue();
				if (jobTodo == null) {
					break;
				}
				try {
					System.out.println(this.getName() + " execute la job\t[" +jobTodo.toString()+"] \t+");
					
					jobTodo.run();
					
					System.out.println(this.getName() + " a fini la job\t[" +jobTodo.toString()+"] \t-");
				} catch (Throwable t) {
					// ignore
				}
			}
		}
	}
	
	private class LoadManagementThread extends Thread {

		public void run() {
			while (true) {
				//Controle du workLoad
				double poolLoad;
				double workingThreads=0;
				double sleepingThreads =0;
				for (int i = 0; i < threadsDuPool.GetSize(); i++) {
					if((threadsDuPool.getAt(i).getState() == this.getState().RUNNABLE)||
							(threadsDuPool.getAt(i).getState() == this.getState().BLOCKED) ||
							(threadsDuPool.getAt(i).getState() == this.getState().TIMED_WAITING)){
						workingThreads++;
					}
					else {sleepingThreads++;}
						
					//System.out.println(threadsDuPool.getAt(i).getState());
						
				}
				try {

					poolLoad = (workingThreads/(workingThreads+sleepingThreads))*100;
				} catch (ArithmeticException e) {
					poolLoad =0 ;
				}
				
				
				System.out.println("<Le Pool Load est de " + poolLoad +"%   -> "+threadsDuPool.GetSize() + " Threads dans le pool" 
						+ "  -> " + jobQueue.GetSize() + " Jobs en attentes>");
				
				//si le thread load est trop haut, on cree plus de thread, sinon on kill dans la limite des bornes
				if((poolLoad>80) && (threadsDuPool.GetSize()< NbMaxThread))
				{
					threadsDuPool.enqueue(new ThreadDuPool());
					threadsDuPool.getAt(threadsDuPool.GetSize()-1).setName("Thread: "+threadsDuPool.GetSize());
					threadsDuPool.getAt(threadsDuPool.GetSize()-1).start();
				}
				
				//si le thread load est trop haut, on cree plus de thread, sinon on kill dans la limite des bornes
				if((poolLoad<70) && (threadsDuPool.GetSize()>NbMinThread ))
				{
					threadsDuPool.dequeueLast().stop();

				}
				
				
				try {
					this.sleep(500);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				
			}
		}
	}
}
